package com.dsfa.wsServ;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.webservice.SoapClient;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.dsfa.biz.busCenter.BusService;
import com.dsfa.biz.dataCenter.AgentLogWSDto;
import com.dsfa.biz.dataCenter.DataCenterService;
import com.dsfa.conf.Config;
import com.dsfa.platform.starter.db.jfinal.plugin.activerecord.Record;
import com.dsfa.platform.starter.redis.RedisExecute;
import com.dsfa.platform.starter.web.configuration.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import java.util.*;

/**
 * @Description: 代理服务
 * @ClassName: IWSServerImpl
 * @Author: wangdm
 * @Date: 2024/1/23
 */
@Slf4j
@WebService(serviceName = "AgentWSService"
        , targetNamespace = "http://wsServ.dsfa.com"
        , endpointInterface = "com.dsfa.wsServ.IWSServer"
        , portName = "IWSServer"
        , name = "AgentWSService"
)
public class WSServerImpl implements IWSServer {
    @Autowired
    private DataCenterService dataCenterService;
    @Autowired
    private BusService busServer;
    @Autowired
    private Config config;

    @Resource
    private WebServiceContext wsContext;

    @Override
    public String wsServerJsIn(String verifyJson, String paramJson) {
        //public String wsServerMap(String timestamp, String token, String app, String vcode, String paramJson) {
        String logPre = "ws代理 [wsServerMap] ";
        log.info("exce {}：\n verifyJson：{} \n paramJson:{}", logPre, verifyJson, paramJson);
        AgentLogWSDto aLogDto = null;
        String resStr = "";
        String verifyRes = "";
        String remark = "";
        try {
            //1、鉴权校验
            JSONObject vjs = JSONObject.parseObject(verifyJson);
            //初始化记录日志
            HttpServletRequest request = (HttpServletRequest) wsContext.getMessageContext().get(MessageContext.SERVLET_REQUEST);
            aLogDto = new AgentLogWSDto(vjs, paramJson, request);
            log.info("{}日志初始化：{}", logPre, aLogDto);
            //在业务库中根据app（appcode）获取appkey
            Record appInfo = busServer.getAppInfoByCode(vjs.getString("app"));
            if (appInfo == null) {
                log.warn("{}app不存在", logPre);
                return "app不存在";
            }
            String appKey = appInfo.getStr("appKey");
            String appName = appInfo.getStr("appName");
            verifyRes = verifyAuth(vjs, appKey);
            aLogDto.setAppKey(appKey);
            aLogDto.setAppName(appName);
            aLogDto.setValid("ok".equals(verifyRes) ? 1 : 0);//验证是否通过
            aLogDto.setValidMsg(verifyRes);
            if (!"ok".equals(verifyRes)) {
                log.warn("{}校验失败：{}：{}", logPre, verifyRes, vjs);
                return verifyRes;
            }
            //2、查询调用wsdl地址，接口及命名空间等信息
            Record tInfo = dataCenterService.getTargetInfoById(vjs.getString("id"));
            if (tInfo == null) {
                log.warn("{}调用接口不存在", logPre);
                return "调用接口不存在";
            }
            aLogDto.setReqInterface(tInfo);
            String webUrl = tInfo.getStr("targetUrl");
            String interfaceMethod = tInfo.getStr("interfaceMethod");//接口
            String targetNamespaceUrl = tInfo.getStr("targetNamespaceUrl");//命名空间
            if (StringUtils.isEmpty(targetNamespaceUrl)) {//如果接口管理表没有命名空间，就用代理管理表中的该字段
                targetNamespaceUrl = tInfo.getStr("targetNamespaceUrlb");//命名空间
            }
            if (StringUtils.isAnyEmpty(webUrl, interfaceMethod, targetNamespaceUrl)) {
                log.warn("{}未找到{}对应目标地址", logPre, vjs.getString("id"));
                return String.format("%s未找到%s对应目标地址", logPre, vjs.getString("id"));
            }
            //3、调用与返回
            //String url = "http://10.5.42.89:9998/mdm/ws/intfsServiceWS?wsdl";
            SoapClient soapClient = SoapClient.create(webUrl);
            // 设置SOAPAction
            soapClient.setMethod(interfaceMethod, targetNamespaceUrl);
            // 设置请求参数
            //Map<String, Object> para = JSONObject.parseObject(paramJson).getInnerMap();
            Map para = JSONObject.parseObject(paramJson,new TypeReference<HashMap<String, String>>() {}.getType());
            log.info("{}本次请求参数：{}", logPre, para);
            soapClient.setParams(para);
            // 发送请求，并获取响应结果
            resStr = soapClient.send(false);
            log.info("{}本次请求返回结果：{}", logPre, resStr);
            int start = resStr.indexOf("<return>");
            int end = resStr.indexOf("</return>");
            if (start > 0 && end > start) {
                resStr = resStr.substring(start + 8, end);
                resStr = StringEscapeUtils.unescapeXml(resStr);
                try {
                    resStr = XmlUtil.format(resStr);
                } catch (Exception e) {
                    log.error("{}结果数据解析异常：{}，仍然返回：{}", logPre, e.getMessage(), resStr);
                    e.printStackTrace();
                    // throw new Fault(new IllegalArgumentException(e.getMessage()));
                }
            }
            aLogDto.setRepData(resStr);
            return resStr;
        } catch (Exception e) {
            e.printStackTrace();
            remark = e.getMessage();
            log.error("{}error：{}", logPre, e.getMessage());
            return e.getMessage();
        } finally {
            //4、日志入库
            if (aLogDto != null) {
                aLogDto.setRespTime((int) ((System.currentTimeMillis() - aLogDto.getTimeStart()) / 1000));
                aLogDto.setRemark(remark);
                aLogDto.setRepStatus(((HttpServletResponse) wsContext.getMessageContext().get(MessageContext.SERVLET_RESPONSE)).getStatus() + "");
                aLogDto.setReqEndTime(DateTime.now());
                //日志入库
                dataCenterService.saveAgentWSLog(aLogDto.toAddRecord());
                log.info("{}日志入库：{}", logPre, aLogDto.getId());
               /* List<Header> headers = (List<Header>) wsContext.getMessageContext().get(Header.HEADER_LIST);
                QName qNameID = new QName("logId");
                Document docID = DOMUtils.createDocument();
                Element elID = docID.createElement("logId");
                elID.setTextContent(aLogDto.getId());
                SoapHeader headerID = new SoapHeader(qNameID, elID);
                //添加开始时间
                QName qNameTimeStart = new QName("timeStart");
                Document docT = DOMUtils.createDocument();
                Element elT = docT.createElement("timeStart");
                elT.setTextContent(aLogDto.getTimeStart() + "");
                SoapHeader headerT = new SoapHeader(qNameTimeStart, elT);

                //这样添加的header，在pre_stream时能获取到
                headers.add(headerID);
                headers.add(headerT);

                log.info("{}日志id写入header，出拦截器解析后再更新相应数据：{}", logPre, aLogDto.getId());*/
                /*aLogDto.setResData((HttpServletResponse) wsContext.getMessageContext().get(MessageContext.SERVLET_RESPONSE)
                        , resStr, verifyRes);
                //日志入库
                boolean b = dataCenterService.saveAgentWSLog(aLogDto.toAddRecord());
                log.info("{}日志入库：{}", logPre, b);*/
            }
        }
    }

    /**
     * @Description: 校验认证
     * @Author: wangdm
     * @Date: 2024/1/12
     */
    private String verifyAuth(JSONObject vjs, String appKey) {
        long timestamp = vjs.getLongValue("timestamp");
        String token = vjs.getString("token");
        String app = vjs.getString("app");
        String vcode = vjs.getString("vcode");
        if (timestamp == 0 || StringUtils.isAnyEmpty(token, app, vcode)) {
            return "verifyJson有空值，请检查";
        }

        //验证规则
        //1、timestamp在 5 * 60000 五分钟之内
        // 创建 Calendar 对象并设置时区为东八区
        if (config.isCheckTimeToken()) {
            Calendar calendar = Calendar.getInstance();
            TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
            calendar.setTimeZone(timeZone);
            // 获取东八区的时间戳
            long time8 = calendar.getTimeInMillis() / 1000 * 1000;
            System.out.println("东八区时间戳：" + time8);
            //与服务器时区不同，先不管时间
            //long time = System.currentTimeMillis();
            long sjc = time8 - timestamp;
            if (sjc > 10 * 60000 || sjc < -10 * 60000) {
                log.info("timestamp超时{}", timestamp);
                return "timestamp超时";
            }
        }
        //2、
        //3、redis 判断token是否已使用
        //token在24小时内只能使用一次
        if (config.isCheckTimeToken()) {
            RedisExecute redis = SpringContextHolder.getBean(RedisExecute.class);
            if (redis.hasKey("24-" + token)) {
                return "token失效：" + token;
            } else {
                redis.set("24-" + token, 1, 24 * 3600);
            }
        }
        //3、判断vcode==md5(timestamp+appcode+token+appkey)
        //TODO token 记入redis，同一个token值不能在24小时内重复使用
        String d5 = DigestUtils.md5Hex(timestamp + app + token + appKey);
        if (StringUtils.isEmpty(vcode) || !vcode.equals(d5)) {
            return "校验失败：" + vjs;
        }
        log.info("Verify OK：{}", vjs);
        return "ok";
    }
//    @Override
//    public String wsServer(String queryData, String webUrl) {
//        OkHttpClient client = new OkHttpClient().newBuilder().build();
//        MediaType mediaType = MediaType.parse("text/xml;charset=utf-8");
//        RequestBody body = RequestBody.create(mediaType, queryData);
//        Request request = new Request.Builder()
//                .url(webUrl).method("POST", body)
//                .addHeader("Content-Type", "text/xml;charset=utf-8")
//                .addHeader("Accept", "*/*")
//                .build();
//        Response response = null;
//        String str = "";
//        try {
//            response = client.newCall(request).execute();
//            str = response.body().string();
//        } catch (IOException e) {
//            log.error(e.getMessage());
//            throw new RuntimeException(e);
//        }
//        //List<Header> headers = (List<Header>) wsContext.getMessageContext().get(Header.HEADER_LIST);
//        return str;
//    }
}